// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#pragma once

#include <melon/random.h>
#include <limits>
#include <optional>
#include <type_traits>

#include <pollux/buffer/string_view_buffer_holder.h>
#include <pollux/common/base/exceptions.h>
#include <pollux/type/cpp_to_type.h>
#include <pollux/type/decimal_util.h>
#include <pollux/common/strings/string_view.h>
#include <pollux/type/type.h>

namespace kumo::pollux::test {

class VectorValueGenerator {
 private:
  static uint32_t getRand32(
      std::optional<melon::Random::DefaultGenerator>& rng,
      uint32_t max = std::numeric_limits<uint32_t>::max()) {
    if (rng.has_value()) {
      return melon::Random::rand32(max, rng.value());
    } else {
      return melon::Random::rand32(max);
    }
  }

  static uint64_t getRand64(
      std::optional<melon::Random::DefaultGenerator>& rng,
      uint64_t max = std::numeric_limits<uint64_t>::max()) {
    if (rng.has_value()) {
      return melon::Random::rand64(max, rng.value());
    } else {
      return melon::Random::rand64(max);
    }
  }

  static double getRandDouble(
      std::optional<melon::Random::DefaultGenerator>& rng) {
    auto max = static_cast<double>(std::numeric_limits<int32_t>::max());
    auto min = static_cast<double>(std::numeric_limits<int32_t>::min());
    if (rng.has_value()) {
      return melon::Random::randDouble(min, max, rng.value());
    } else {
      return melon::Random::randDouble(min, max);
    }
  }

 public:
  template <typename T>
  static T cardValueOf(
      bool useFullTypeRange,
      std::optional<melon::Random::DefaultGenerator>& rng,
      StringViewBufferHolder& stringViewBufferHolder,
      std::optional<uint32_t> fixedWidthStringSize = std::nullopt,
      const TypePtr& type = CppToType<T>::create()) {
    if constexpr (std::is_same_v<T, int128_t>) {
      auto upper = useFullTypeRange ? getRand64(rng) : getRand32(rng);
      auto lower = useFullTypeRange ? getRand64(rng) : getRand32(rng);
      if (type->isLongDecimal()) {
        const auto& [precision, _] = getDecimalPrecisionScale(*type);
        return HugeInt::build(upper, lower) %
            DecimalUtil::kPowersOfTen[precision];
      } else {
        return HugeInt::build(upper, lower);
      }
    } else if constexpr (
        std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>) {
      auto value = useFullTypeRange ? getRand64(rng) : getRand32(rng);
      if (type->isShortDecimal()) {
        const auto& [precision, _] = getDecimalPrecisionScale(*type);
        return value % DecimalUtil::kPowersOfTen[precision];
      } else {
        return value;
      }
    } else if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) {
      // The other integral cases, signed and unsigned
      auto max = std::numeric_limits<T>::max();
      if (!useFullTypeRange) {
        if constexpr (
            std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t>) {
          max = std::numeric_limits<int16_t>::max();
        } else if constexpr (
            std::is_same_v<T, int16_t> || std::is_same_v<T, uint16_t>) {
          max = std::numeric_limits<int8_t>::max();
        } else {
          max = std::numeric_limits<int8_t>::max() / 2;
        }
      }
      return getRand32(rng, max);
    } else if constexpr (std::is_same_v<T, double>) {
      return getRandDouble(rng);
    } else if constexpr (std::is_same_v<T, StringView>) {
      auto str = std::string(
          fixedWidthStringSize.has_value() ? fixedWidthStringSize.value()
                                           : getRand32(rng) % 100 + 1,
          'a' + (getRand32(rng) % 26));
      return stringViewBufferHolder.getOwnedValue(StringView(str));
    } else if constexpr (std::is_same_v<T, bool>) {
      return getRand32(rng) % 2 == 0;
    } else {
      POLLUX_UNSUPPORTED("Invalid type");
    }
  }

  template <typename T>
  static T nullValueOf() {
    if constexpr (std::is_arithmetic<T>::value) {
      // integers, double and bool
      return T{};
    } else if constexpr (std::is_same_v<T, StringView>) {
      return StringView();
    } else {
      POLLUX_UNSUPPORTED("Invalid type");
    }
  }
};

} // namespace kumo::pollux::test
